<template>
  <div class="three-wrapper">
    <div class="box" ref="box"></div>
  </div>
</template>

<script>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

const clock = new THREE.Clock()
const FPS = 30
const renderT = 1 / FPS
let timeS = 0

export default {
  components: {},
  props: {},
  data() {
    return {
      el: null,
      scene: null,
      camera: null,
      renderer: null,
      controls: null,
      animationFrameId: null,
      raycaster: null, // 光线投射器，用于检测点击
      mouse: null, // 鼠标坐标
      mesh: null // 保存mesh引用
    }
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {
    this.el = this.$refs['box']
    this.clock = new THREE.Clock()

    this.initThree()
    this.bindEvents()
  },
  beforeDestroy() {
    // 清理事件监听
    if (this.renderer && this.renderer.domElement) {
      this.renderer.domElement.removeEventListener('click', this.onCanvasClick)
    }
    if (this.animationFrameId) {
      cancelAnimationFrame(this.animationFrameId)
    }
  },
  methods: {
    initThree() {
      this.initScene()
      this.initCamera()
      this.initRender()
      this.initLight()
      this.initControls()
      this.initRaycaster() // 初始化光线投射器
      this.animate()

      this.addMesh()
    },

    // 初始化光线投射器
    initRaycaster() {
      this.raycaster = new THREE.Raycaster()
      this.mouse = new THREE.Vector2()
    },

    // 绑定事件
    bindEvents() {
      // 监听canvas的点击事件
      this.renderer.domElement.addEventListener('click', this.onCanvasClick)
    },

    // 点击事件处理
    onCanvasClick(event) {
      // 计算鼠标在归一化设备坐标中的位置 (-1 到 +1)
      const rect = this.renderer.domElement.getBoundingClientRect()
      this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1
      this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1

      // 通过摄像机和鼠标位置更新射线
      this.raycaster.setFromCamera(this.mouse, this.camera)

      // 计算物体和射线的交点
      const intersects = this.raycaster.intersectObjects(this.scene.children)

      // 检查是否点击到了mesh
      if (intersects.length > 0) {
        const clickedObject = intersects[0].object

        console.log('clickedObject', clickedObject)

        // 判断点击的是否是我们的立方体
        if (clickedObject === this.mesh) {
          this.onMeshClick()
        }
      }
    },

    // mesh点击事件处理
    onMeshClick() {
      console.log('立方体被点击了!')
      // 改变立方体颜色
      this.mesh.material.color.set(new THREE.Color(Math.random() * 0xffffff))
    },

    addMesh() {
      const geometry = new THREE.BoxGeometry(10, 10, 10)
      const material = new THREE.MeshLambertMaterial({
        color: new THREE.Color('#00ff00'),
        transparent: true
      })
      this.mesh = new THREE.Mesh(geometry, material)
      this.scene.add(this.mesh)

      // 为mesh添加用户数据，可以用于标识
      this.mesh.userData = {
        name: 'clickableCube',
        clickable: true
      }
    },

    // 初始化场景
    initScene() {
      this.scene = new THREE.Scene()
    },

    // 初始化相机
    initCamera() {
      const asp = this.el.offsetWidth / this.el.offsetHeight
      this.camera = new THREE.PerspectiveCamera(45, asp, 0.1, 10000000)
      this.camera.position.set(300, 300, 300)
      this.camera.lookAt(this.scene.position)
      this.scene.add(this.camera)
    },

    initRender(clearColor = '#000') {
      const width = this.el.offsetWidth
      const height = this.el.offsetHeight
      this.renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true
      })
      this.renderer.setPixelRatio(window.devicePixelRatio)
      this.renderer.setSize(width, height)
      this.el.append(this.renderer.domElement)
      this.renderer.setClearColor(clearColor, 1)

      // 设置canvas可点击样式
      this.renderer.domElement.style.cursor = 'pointer'
    },

    initLight() {
      const ambientLight = new THREE.AmbientLight('#fff', 0.3)
      this.scene.add(ambientLight)
      const directionalLight = new THREE.DirectionalLight('#fff')
      directionalLight.position.set(30, 30, 30).normalize()
      this.scene.add(directionalLight)
    },

    initControls() {
      this.controls = new OrbitControls(
        this.camera,
        this.renderer.domElement,
        {}
      )
    },

    render() {
      const dt = this.clock.getDelta()
      const T = clock.getDelta()
      timeS = timeS + T
      if (timeS > renderT) {
        this.controls.update(dt)
        this.renderer.render(this.scene, this.camera)
      }
    },

    animate() {
      this.render()
      this.animationFrameId = requestAnimationFrame(this.animate)
    }
  }
}
</script>

<style scoped lang="scss">
.three-wrapper {
  height: 100%;
  position: relative;
  .box {
    height: 100%;
  }
}
</style>
